#include <sdl.h>

struct sdl_t *sdl_init(int width, int height, int bpp, int flag)
{
    struct sdl_t *sdl = NULL;
    int ret, t = SDL_INIT_VIDEO;

    sdl = (struct sdl_t *)malloc(sizeof(struct sdl_t));
    SDL_ERRP(NULL == sdl, goto ERR1, 1, "malloc sdl!\n");

    memset(sdl, 0, sizeof(struct sdl_t));

    sdl->key_head.next = &sdl->key_head;
    sdl->key_head.prev = &sdl->key_head;

    sdl->mouse_head.next = &sdl->mouse_head;
    sdl->mouse_head.prev = &sdl->mouse_head;

    if (flag & KYO_MUSIC || flag & KYO_CHUNK)
    {
        t |= SDL_INIT_AUDIO;
        sdl->audio = 1;
    }

    ret = SDL_Init(t);
    SDL_ERRP(-1 == ret, goto ERR2, 1, "SDL_Init!\n");

    t = SDL_SWSURFACE;
    if (flag & KYO_FULLSCREEN)
        t |= SDL_FULLSCREEN;

    sdl->screen = SDL_SetVideoMode(width, height, bpp, t);
    SDL_ERRP(NULL == sdl->screen, goto ERR2, 1, "SDL_SetVideoMode!\n");

    if (flag & KYO_TTF)
    {
        sdl->ttf = 1;
        ret = TTF_Init();
        SDL_ERRP(-1 == ret, goto ERR2, 1, "TTF_Init!\n");
    }

    if (flag & KYO_NET)
    {
        sdl->net = 1;
        ret = SDLNet_Init();
        SDL_ERRP(-1 == ret, goto ERR2, 1, "SDLNet_Init!\n");
    }

    if (flag & KYO_MUSIC)
    {
        ret = Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, 512);
        SDL_ERRP(-1 == ret, goto ERR2, 1, "Mix_OpenAudio!\n");
    }
    else if (flag & KYO_CHUNK)
    {
        ret = Mix_OpenAudio(22050, AUDIO_S16, 1, 256);
        SDL_ERRP(-1 == ret, goto ERR2, 1, "Mix_OpenAudio!\n");
    }

    register_key(sdl, SDLK_ESCAPE, KYO_QUIT, NULL);

    return sdl;
ERR2:
    sdl_exit(sdl);    
ERR1:    
    return NULL;
}

void sdl_exit(struct sdl_t *sdl)
{
    struct key_node_t *ktail = sdl->key_head.next, *ksave = NULL;
    struct mouse_node_t *mtail = sdl->mouse_head.next, *msave = NULL;

    if (sdl->ttf)
        TTF_Quit();
    if (sdl->audio)
        Mix_CloseAudio();
    if (sdl->net)
        SDLNet_Quit();
    SDL_FreeSurface(sdl->screen);

    while (ktail != &sdl->key_head)
    {
        ksave = ktail->next;
        free(ktail);
        ktail = ksave;
    }
    while (mtail != &sdl->mouse_head)
    {
        msave = mtail->next;
        free(mtail);
        mtail = msave;
    }

    free(sdl);
    SDL_Quit();
}

int register_key(struct sdl_t *sdl, int key, key_handle_t *handle, void *data)
{
    struct key_node_t *new = NULL;

    for (new = sdl->key_head.next; new != &sdl->key_head; new = new->next)
    {
        if (key == new->key)
        {
            new->handle = handle;    
            new->data = data;
            return 0;
        }
    }

    new = (struct key_node_t *)malloc(sizeof(struct key_node_t));
    SDL_ERRP(NULL == new, goto ERR1, 1, "malloc");

    new->key = key;
    new->handle = handle;
    new->data = data;

    new->next = &sdl->key_head;
    new->prev = sdl->key_head.prev;
    sdl->key_head.prev->next = new;
    sdl->key_head.prev = new;

    return 0;
ERR1:
    return -1;
}

int register_mouse(struct sdl_t *sdl, int button, mouse_handle_t *handle, void *data)
{
    struct mouse_node_t *new = NULL;

    /*
     *for (new = sdl->mouse_head.next; new != &sdl->mouse_head; new = new->next)
     *{
     *    if (button == new->button)
     *    {
     *        new->handle = handle;    
     *        new->data = data;
     *        return 0;
     *    }
     *}
     */
    new = (struct mouse_node_t *)malloc(sizeof(struct mouse_node_t));
    SDL_ERRP(NULL == new, goto ERR1, 1, "malloc");

    if (sdl->mouse_head.prev == &sdl->mouse_head)
        new->id = 1;
    else
        new->id = sdl->mouse_head.prev->id + 1;
    new->button = button;
    new->handle = handle;
    new->data = data;

    new->next = &sdl->mouse_head;
    new->prev = sdl->mouse_head.prev;
    sdl->mouse_head.prev->next = new;
    sdl->mouse_head.prev = new;

    return new->id;
ERR1:
    return -1;
}

void destroy_mouse(struct sdl_t *sdl, int id)
{
    struct mouse_node_t *mtail = NULL;

    for (mtail = sdl->mouse_head.next; mtail != &sdl->mouse_head; mtail = mtail->next)
    {
        if (id == mtail->id)
        {
            mtail->next->prev = mtail->prev;
            mtail->prev->next = mtail->next;
            free(mtail);
            return;
        }
    }
}

int run_key(struct sdl_t *sdl)
{
    SDL_Event event;
    struct key_node_t *ktail = NULL;
    struct mouse_node_t *mtail = NULL;

    while (1)
    {
        if (SDL_WaitEvent(&event))
        {
            if (event.type == SDL_QUIT)
                break;
            if (event.type == SDL_KEYDOWN)
            {
                for (ktail = sdl->key_head.next; ktail != &sdl->key_head; ktail = ktail->next)
                {
                    if (event.key.keysym.sym == ktail->key)
                    {
                        if (ktail->handle == (key_handle_t *)(KYO_QUIT))
                            return 0;
                        ktail->handle(ktail->key, ktail->data);
                    }
                }
            }
            if (event.type == SDL_MOUSEBUTTONDOWN)
            {
                for (mtail = sdl->mouse_head.next; mtail != &sdl->mouse_head; mtail = mtail->next)
                {
                    if (event.button.button == mtail->button)
                        mtail->handle(mtail->button, event.button.x, event.button.y, mtail->data);
                }
            }
        }
    }
    return 0;
}

SDL_Surface *pic_load(const char *file)
{
    SDL_Surface *op = NULL, *img = NULL;

    op = IMG_Load(file);
    if (op != NULL)
    { 
        img = SDL_DisplayFormat(op);
        SDL_FreeSurface(op);
    }

    return img;
}

void show_txt(TTF_Font *font, const char *content, int color, SDL_Surface *screen, int x, int y)
{
    SDL_Surface *txt = NULL;
    SDL_Color c;
    SDL_Rect dst;

    c.r = (char)(color >> 16);
    c.g = (char)(color >> 8);
    c.b = (char)color;

    txt = TTF_RenderUTF8_Blended(font, content, c);

    dst.x = x;
    dst.y = y;
    SDL_BlitSurface(txt, NULL, screen, &dst);

    SDL_FreeSurface(txt);
}

void draw_pixel(SDL_Surface *dst, int x, int y, int color)
{
    SDL_Rect offset;

    offset.x = x;
    offset.y = y;
    offset.w = 1;
    offset.h = 1;

    SDL_FillRect(dst, &offset, SDL_MapRGB(dst->format, (char)(color >> 16), (char)(color >> 8), (char)color));
}

void draw_rect(SDL_Surface *dst, int x, int y, int w, int h, int color)
{
    SDL_Rect offset;

    offset.x = x;
    offset.y = y;
    offset.w = w;
    offset.h = h;

    SDL_FillRect(dst, &offset, SDL_MapRGB(dst->format, (char)(color >> 16), (char)(color >> 8), (char)color));
}

void draw_vline(SDL_Surface *dst, int x, int y, int len, int color)
{
    draw_rect(dst, x, y, 1, len, color);
}

void draw_hline(SDL_Surface *dst, int x, int y, int len, int color)
{
    draw_rect(dst, x, y, len, 1, color);
}


void draw_box(SDL_Surface *dst, int x, int y, int w, int h, int border, int bcolor, int color)
{
    draw_rect(dst, x, y, w, h, bcolor);
    draw_rect(dst, x + border, y + border, w - 2 * border, h - 2 * border, color);
}

void draw_circle(SDL_Surface *dst, int x, int y, int r, int color)
{
    int i, j;

    for (i = y - r; i < y + r; i++)
    {
        for (j = x - r; j < x + r; j++)
        {
            if ((i - y) * (i - y) + (j - x) * (j - x) <= r * r)    
                draw_pixel(dst, i, j, color);
        }
    }
}

